package com.smdk.dsminio.logs;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.map.MapUtil;
import cn.hutool.log.StaticLog;
import com.alibaba.fastjson.JSONObject;
import com.smdk.dsminio.common.CommonVariable;
import com.smdk.dsminio.utils.DateUtil;
import com.smdk.dsminio.utils.GetIPAddress;
import com.smdk.dsminio.vo.LogsInfo;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
@Component
public class LogFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        LogsInfo logInfo = new LogsInfo();
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        String method = serverHttpRequest.getMethodValue().toUpperCase();
        logInfo.setMethod(method);
        logInfo.setHeader(serverHttpRequest.getHeaders().toString());
        logInfo.setUrl(serverHttpRequest.getURI().toString());
        String ip = serverHttpRequest.getHeaders().getFirst("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = serverHttpRequest.getHeaders().getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = serverHttpRequest.getHeaders().getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = serverHttpRequest.getHeaders().getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = serverHttpRequest.getHeaders().getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip != null && ip.indexOf(",") == -1) {
            ip = ip;
        } else {
            ip = "0.0.0.0";
        }
        logInfo.setIp(ip);
        if (!("GET".equals(method) || "DELETE".equals(method))) {
            AtomicReference<Map<String, String>> bodyMap = exchange.getAttribute("cachedRequestBody");
            if (bodyMap!=null && !MapUtil.isEmpty(bodyMap.get())) {
                logInfo.setBodyJson(JSONObject.toJSONString(bodyMap));
                exchange.getAttributes().remove("cachedRequestBody");
            }
        }

        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = serverHttpResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(serverHttpResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.map(dataBuffer -> {
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        DataBufferUtils.release(dataBuffer);
                        String resp = new String(content, StandardCharsets.UTF_8);
                        logInfo.setRequestResult(resp);
                        //日志写入异步线程
                        new Thread(() -> {
                            writeAccessLog(logInfo);
                        }).start();
                        byte[] uppedContent = new String(content, StandardCharsets.UTF_8).getBytes();
                        return bufferFactory.wrap(uppedContent);
                    }));
                }
                return super.writeWith(body);
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    @Async
    public void writeAccessLog(LogsInfo logInfo){
        StaticLog.info(JSONObject.toJSONString(logInfo));
    }


    @Override
    public int getOrder() {
        return -20;
    }
}